//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  ReminderItem.cs
//    
// Abstract:
//
//  This module implements the ReminderItem class. The class encapsulates the details for an enhanced reminder.
//  
//--

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.EnhancedReminders
{
    /// <summary>
    /// Encapsulates an enhanced reminder.
    /// </summary>
    [Serializable()]
    public sealed class ReminderItem
    {
        /// <summary>
        /// Notification channels list
        /// </summary>
        private NotificationChannelList _notificationChannels;

        /// <summary>
        /// Join now channels list
        /// </summary>
        private NotificationChannelListLeadTime _joinNowChannels;

        /// <summary>
        /// Enum indicating presence
        /// </summary>
        private PresenceStates _presence;

        /// <summary>
        /// Conference address
        /// </summary>
        private string _conferenceAddress;

        /// <summary>
        /// Enum indicating conference type
        /// </summary>
        private ConferenceType _conferenceType;

        /// <summary>
        /// Timezone associated with absolute time reminders
        /// </summary>
        private TimeZoneInfo _timeZoneInfo;

        /// <summary>
        /// Ctor that populates the list of channels from a given master list of channels.
        /// </summary>
        /// <param name="masterChannels">The master channels list</param>
        public ReminderItem(IEnumerable<NotificationChannel> masterChannels)
        {
            // Default to using lead time rather than absolute time
            _notificationChannels = new NotificationChannelListLeadTime(masterChannels);
            _joinNowChannels = new NotificationChannelListLeadTime(masterChannels);
        }

        /// <summary>
        /// Copy constructor using lead time.
        /// </summary>
        /// <param name="notificationChannels">Notification channels</param>
        /// <param name="joinNowChannels">JoinNow channels</param>
        /// <param name="timeZoneInfo">Timezone for absolute time reminders</param>
        public ReminderItem(NotificationChannelListLeadTime notificationChannels, NotificationChannelListLeadTime joinNowChannels, TimeZoneInfo timeZoneInfo)
        {
            if (notificationChannels != null)
            {
                _notificationChannels = new NotificationChannelListLeadTime(notificationChannels);
            }

            if (joinNowChannels != null)
            {
                _joinNowChannels = new NotificationChannelListLeadTime(joinNowChannels);
            }

            _timeZoneInfo = timeZoneInfo;
        }

        /// <summary>
        /// Copy constructor using absolute time.
        /// </summary>
        /// <param name="notificationChannels">Notification channels</param>
        /// <param name="joinNowChannels">JoinNow channels</param>
        /// <param name="timeZoneInfo">Timezone for absolute time reminders</param>
        public ReminderItem(NotificationChannelListAbsoluteTime notificationChannels, NotificationChannelListLeadTime joinNowChannels, TimeZoneInfo timeZoneInfo)
        {
            if (notificationChannels != null)
            {
                _notificationChannels = new NotificationChannelListAbsoluteTime(notificationChannels);
            }

            if (joinNowChannels != null)
            {
                _joinNowChannels = new NotificationChannelListLeadTime(joinNowChannels);
            }

            _timeZoneInfo = timeZoneInfo;
        }

        /// <summary>
        /// Gets the due time for this reminder given the specified meeting start time.
        /// </summary>
        /// <param name="startTime">Start time of the meeting associated with this reminder</param>
        /// <param name="joinNow">Indicates if the JoinNow or reminder due time is required</param>
        /// <returns>
        /// The UTC time that the reminder / JoinNow is due.
        /// </returns>
        public DateTime GetDueTime(DateTime startTime, bool joinNow)
        {
            DateTime dueTime;

            NotificationChannelList channelList = joinNow ? _joinNowChannels : _notificationChannels;

            NotificationChannelListAbsoluteTime absoluteTimeChannels = channelList as NotificationChannelListAbsoluteTime;

            if (absoluteTimeChannels != null)
            {
                Debug.Assert(_timeZoneInfo != null, "_timeZoneInfo != null");
                Debug.Assert(!joinNow, "!joinNow");

                // Convert the meeting start time into the appropriate local timezone
                DateTime localStartTime = TimeZoneInfo.ConvertTimeFromUtc(startTime, _timeZoneInfo);

                // If the reminder has an absolute time, use the specified hours and minutes
                DateTime localDueTime = new DateTime(localStartTime.Year, localStartTime.Month, localStartTime.Day, 
                    absoluteTimeChannels.AbsoluteTime.Hour, absoluteTimeChannels.AbsoluteTime.Minute, 
                    localStartTime.Second);

                if (localDueTime > localStartTime)
                {
                    // Ensure the reminder is always before the meeting
                    localDueTime = localDueTime.AddDays(-1);
                }

                // Convert the reminder due time back to UTC time
                dueTime = TimeZoneInfo.ConvertTimeToUtc(localDueTime, _timeZoneInfo);
            }
            else
            {
                // If the reminder/JoinNow has a lead time, subtract the lead time from the start time for the meeting
                NotificationChannelListLeadTime leadTimeChannels = channelList as NotificationChannelListLeadTime;
                Debug.Assert(leadTimeChannels != null, "leadTimeChannels != null");
                dueTime = startTime - TimeSpan.FromMinutes(leadTimeChannels.LeadTime > 0 ? leadTimeChannels.LeadTime : 0);
            }

            Debug.Assert(dueTime <= startTime, "dueTime <= startTime");

            return dueTime;
        }

        /// <summary>
        /// Indicates if an enhanced reminder is enabled.
        /// </summary>
        public bool IsNotificationEnabled
        {
            get
            {
                return _notificationChannels != null && _notificationChannels.IsEnabled;
            }
        }

        /// <summary>
        /// Indicates if JoinNow is enabled.
        /// </summary>
        public bool IsJoinNowEnabled
        {
            get
            {
                return _joinNowChannels != null && _joinNowChannels.IsEnabled;
            }
        }

        /// <summary>
        /// Indicates if this reminder is enabled.
        /// </summary>
        public bool IsEnabled
        {
            get
            {
                return IsNotificationEnabled || IsJoinNowEnabled;
            }
        }

        /// <summary>
        /// The list of notification channels and associated lead time (in minutes).
        /// </summary>
        public NotificationChannelList NotificationChannelList
        {
            get
            {
                return _notificationChannels;
            }
            set
            {
                _notificationChannels = value;
            }
        }

        /// <summary>
        /// The list of join now channels and associated lead time (in minutes).
        /// </summary>
        public NotificationChannelListLeadTime JoinNowChannelList
        {
            get
            {
                return _joinNowChannels;
            }
            set
            {
                _joinNowChannels = value;
            }
        }

        /// <summary>
        /// Address of Conference in SIP format
        /// </summary>
        public string ConferenceAddress
        {
            get
            {
                return _conferenceAddress;
            }
            set
            {
                _conferenceAddress = value;
            }
        }

        /// <summary>
        /// Conference Type
        /// </summary>
        public ConferenceType ConferenceType
        {
            get
            {
                return _conferenceType;
            }
            set
            {
                _conferenceType = value;
            }
        }

        /// <summary>
        /// Presence flags
        /// </summary>
        public PresenceStates Presence
        {
            get
            {
                return _presence;
            }
            set
            {
                _presence = value;
            }
        }

        /// <summary>
        /// Returns whether or not this reminder item will require a presence lookup.
        /// </summary>
        /// <returns>True if there are any presence conditions or non-custom channels</returns>
        public bool RequiresLookup()
        {
            if (NotificationChannelList != null)
            {
                foreach (NotificationChannel channel in NotificationChannelList.Channels)
                {
                    if (!(channel is NotificationCustomChannel) && channel.IsEnabled)
                    {
                        return true;
                    }
                }
            }

            if (JoinNowChannelList != null)
            {
                foreach (NotificationChannel channel in JoinNowChannelList.Channels)
                {
                    if (!(channel is NotificationCustomChannel) && channel.IsEnabled)
                    {
                        return true;
                    }
                }
            }

            return Presence != PresenceStates.None;
        }
    }
}
